/*
 * ActionComponentDelegate.h  0.4.0 / Dec 04, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
#include "ActionComponentDelegate.h"

#include "QCursorAdapter.h"

// ActionComponentData :: Protected Constructor. -------------------------------

/*!
 * \brief Constructs new ActionComponentData object.
 */
ActionComponentData::ActionComponentData()
  : focusable(false), opaque(true), updatesEnabled(true), orientation(4),
    maximumSize(-1, -1), minimumSize(-1, -1), fixedSize(-1, -1)
{
  // empty constructor body
}

// ActionComponentDelegate :: Protected Constructors. --------------------------

/*!
 * \brief Constructs new QActionComponentDelegate object.
 *
 * \param env the JNIEnv pointer
 * \param obj the Component java object
 * \param action the action
 */
ActionComponentDelegate::ActionComponentDelegate(JNIEnv *env, jobject obj,
                                                 QAction *action)
  : ComponentDelegate(env, obj, action), m_makeVisible(false)
{
  // empty constructor body
}

/*!
 * \brief Constructs new QActionComponentDelegate object.
 *
 * \param env the JNIEnv pointer
 * \param obj the Component java object
 */
ActionComponentDelegate::ActionComponentDelegate(JNIEnv *env, jobject obj)
  : ComponentDelegate(env, obj, (QAction *)0), m_makeVisible(false)
{
  // empty constructor body
}

// ActionComponentDelegate :: Protected Destructor. ----------------------------

/*!
 * \brief Destroys the ActionComponentDelegate object.
 */
ActionComponentDelegate::~ActionComponentDelegate()
{
  if (m_action) {
    deleteQAction(m_action);
    m_action = 0;
  }
  if (m_widget) {
    deleteQWidget(m_widget);
    m_widget = 0;
  }
}

// ActionComponentDelegate :: Overridden Native Calls. -------------------------

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setVisible(JNIEnv *env, jboolean visible)
{
  Q_ASSERT(env);
  SWING_REENTRANT(setVisible, SWING_ENV_ARG, JBOOLEAN_ARG(visible))
  if (isQWidget()) {
    ComponentDelegate::setVisible(env, visible);
  } else {
    action()->setVisible(visible);
    visibleChanged(visible);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::showWindow(JNIEnv *env)
{
  Q_ASSERT(env);
  SWING_REENTRANT(showWindow, SWING_ENV_ARG)
  switchToQWidget();
  ComponentDelegate::showWindow(env);
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setEnabled(JNIEnv *env, jboolean enabled)
{
  Q_ASSERT(env);
  SWING_REENTRANT(setEnabled, SWING_ENV_ARG, JBOOLEAN_ARG(enabled))
  if (isQWidget()) {
    ComponentDelegate::setEnabled(env, enabled);
  } else {
    action()->setEnabled(enabled);
    enabledChanged(enabled);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setFocusable(JNIEnv *env, jboolean focusable)
{
  Q_ASSERT(env);
  SWING_REENTRANT(setFocusable, SWING_ENV_ARG, JBOOLEAN_ARG(focusable))
  if (isQWidget()) {
    ComponentDelegate::setFocusable(env, focusable);
  } else {
    actionData()->focusable = focusable;
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::requestFocus(JNIEnv *env)
{
  Q_ASSERT(env);
  SWING_REENTRANT(requestFocus, SWING_ENV_ARG)
  if (isQWidget()) {
    ComponentDelegate::requestFocus(env);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setLocation(JNIEnv *env, jint x, jint y)
{
  Q_ASSERT(env);
  SWING_REENTRANT(setLocation, SWING_ENV_ARG, JINT_ARG(x), JINT_ARG(y))
  if (isQWidget()) {
    ComponentDelegate::setLocation(env, x, y);
  }
}

/*!
 * \inheritDoc
 */
jobject ActionComponentDelegate::getPreferredSize(JNIEnv *env)
{
  Q_ASSERT(env);
  SWING_REENTRANT_RETURN(getPreferredSize, jobject, SWING_ENV_ARG)
  if (isQWidget()) {
    return ComponentDelegate::getPreferredSize(env);
  } else {
    return DimensionObject::newObject(0, 0);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setSize(JNIEnv *env, jint width, jint height)
{
  Q_ASSERT(env);
  SWING_REENTRANT(setSize, SWING_ENV_ARG, JINT_ARG(width), JINT_ARG(height))
  if (isQWidget()) {
    ComponentDelegate::setSize(env, width, height);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setMaximumSize(JNIEnv *env, jint width,
                                             jint height)
{
  Q_ASSERT(env);
  SWING_REENTRANT(setMaximumSize, SWING_ENV_ARG, JINT_ARG(width),
                   JINT_ARG(height))
  if (isQWidget()) {
    ComponentDelegate::setMaximumSize(env, width, height);
  } else {
    actionData()->maximumSize = QSize(width, height);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setMinimumSize(JNIEnv *env, jint width,
                                             jint height)
{
  Q_ASSERT(env);
  SWING_REENTRANT(setMinimumSize, SWING_ENV_ARG, JINT_ARG(width),
                  JINT_ARG(height))
  if (isQWidget()) {
    ComponentDelegate::setMinimumSize(env, width, height);
  } else {
    actionData()->minimumSize = QSize(width, height);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setFixedSize(JNIEnv *env, jint width, jint height)
{
  Q_ASSERT(env);
  SWING_REENTRANT(setFixedSize, SWING_ENV_ARG, JINT_ARG(width),
                  JINT_ARG(height))
  if (isQWidget()) {
    ComponentDelegate::setFixedSize(env, width, height);
  } else {
    actionData()->fixedSize = QSize(width, height);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setBounds(JNIEnv *env, jint x, jint y, jint width,
                                        jint height)
{
  Q_ASSERT(env);
  SWING_REENTRANT(setBounds, SWING_ENV_ARG, JINT_ARG(x), JINT_ARG(y),
                  JINT_ARG(width), JINT_ARG(height))
  if (isQWidget()) {
    ComponentDelegate::setBounds(env, x, y, width, height);
  } else {
    actionData()->bounds = QRect(x, y, width, height);
  }
}

/*!
 * \inheritDoc
 */
jobject ActionComponentDelegate::getBackground(JNIEnv *env, jclass colorClass)
{
  Q_ASSERT(env);
  Q_ASSERT(colorClass);
  SWING_REENTRANT_RETURN(getBackground, jobject, SWING_ENV_ARG,
                         JCLASS_ARG(colorClass))
  if (isQWidget()) {
    return ComponentDelegate::getBackground(env, colorClass);
  } else {
    if (actionData()->background.isValid()) {
      return QColorAdapter::toJColor(actionData()->background);
    }
    return QColorAdapter::toJColor(QApplication::palette().
                                                       color(QPalette::Window));
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setBackground(JNIEnv *env, jint argb)
{
  Q_ASSERT(env);
  SWING_REENTRANT(setBackground, SWING_ENV_ARG, JINT_ARG(argb))
  if (isQWidget()) {
    ComponentDelegate::setBackground(env, argb);
  } else {
    actionData()->background = QColorAdapter(argb);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setNullBackground(JNIEnv *env)
{
  Q_ASSERT(env);
  SWING_REENTRANT(setNullBackground, SWING_ENV_ARG)
  if (isQWidget()) {
    ComponentDelegate::setNullBackground(env);
  } else {
    actionData()->background = QApplication::palette().color(QPalette::Window);
  }
}

/*!
 * \inheritDoc
 */
jobject ActionComponentDelegate::getForeground(JNIEnv *env, jclass colorClass)
{
  Q_ASSERT(env);
  Q_ASSERT(colorClass);
  SWING_REENTRANT_RETURN(getForeground, jobject, SWING_ENV_ARG,
                         JCLASS_ARG(colorClass))
  if (isQWidget()) {
    return ComponentDelegate::getForeground(env, colorClass);
  } else {
    if (actionData()->foreground.isValid()) {
      return QColorAdapter::toJColor(actionData()->foreground);
    }
    return QColorAdapter::toJColor(QApplication::palette().
                                                   color(QPalette::WindowText));
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setForeground(JNIEnv *env, jint argb)
{
  Q_ASSERT(env);
  SWING_REENTRANT(setForeground, SWING_ENV_ARG, JINT_ARG(argb))
  if (isQWidget()) {
    ComponentDelegate::setForeground(env, argb);
  } else {
    actionData()->foreground = QColorAdapter(argb);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setNullForeground(JNIEnv *env)
{
  Q_ASSERT(env);
  SWING_REENTRANT(setNullForeground, SWING_ENV_ARG)
  if (isQWidget()) {
    ComponentDelegate::setNullForeground(env);
  } else {
    actionData()->foreground = QApplication::palette().
                                                   color(QPalette::WindowText);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setOpaque(JNIEnv *env, jboolean opaque)
{
  Q_ASSERT(env);
  SWING_REENTRANT(setOpaque, SWING_ENV_ARG, JBOOLEAN_ARG(opaque))
  if (isQWidget()) {
    ComponentDelegate::setOpaque(env, opaque);
  } else {
    actionData()->opaque = opaque;
  }
}

/*!
 * \inheritDoc
 */
jobject ActionComponentDelegate::getFont(JNIEnv *env, jclass fontClass)
{
  Q_ASSERT(env);
  Q_ASSERT(fontClass);
  SWING_REENTRANT_RETURN(getFont, jobject, SWING_ENV_ARG,
                         JCLASS_ARG(fontClass))
  if (isQWidget()) {
    return ComponentDelegate::getFont(env, fontClass);
  } else {
    return QFontAdapter::toJFont(env, fontClass, action()->font());
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setFont(JNIEnv *env, jobject font)
{
  Q_ASSERT(env);
  Q_ASSERT(font);
  SWING_REENTRANT(setFont, SWING_ENV_ARG, JOBJECT_ARG(font))
  if (isQWidget()) {
    ComponentDelegate::setFont(env, font);
  } else {
    action()->setFont(*QFontAdapter::toQFont(env, font));
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setStyleSheet(JNIEnv *env, jstring styleSheetText)
{
  Q_ASSERT(env);
  Q_ASSERT(styleSheetText);
  SWING_REENTRANT(setStyleSheet, SWING_ENV_ARG, JSTRING_ARG(styleSheetText))
  if (isQWidget()) {
    ComponentDelegate::setStyleSheet(env, styleSheetText);
  } else {
    actionData()->styleSheet = QStringAdapter(env, styleSheetText);
  }
}

/*!
 * \inheritDoc
 */
jint ActionComponentDelegate::getCursor(JNIEnv *env)
{
  if (isQWidget()) {
    return ComponentDelegate::getCursor(env);
  } else {
    return QCursorAdapter::cursorType(actionData()->cursor);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setCursor(JNIEnv *env, jint type)
{
  Q_ASSERT(env);
  SWING_REENTRANT(setCursor, SWING_ENV_ARG, JINT_ARG(type))
  if (isQWidget()) {
    ComponentDelegate::setCursor(env, type);
  } else {
    actionData()->cursor = QCursorAdapter(type);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::repaint(JNIEnv *env)
{
  Q_ASSERT(env);
  SWING_REENTRANT(repaint, SWING_ENV_ARG)
  if (isQWidget()) {
    ComponentDelegate::repaint(env);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::repaint(JNIEnv *env, jint x, jint y, jint width,
                                      jint height)
{
  Q_ASSERT(env);
  SWING_REENTRANT(repaint, SWING_ENV_ARG, JINT_ARG(x), JINT_ARG(y),
                  JINT_ARG(width), JINT_ARG(height))
  if (isQWidget()) {
    ComponentDelegate::repaint(env, x, y, width, height);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::update(JNIEnv *env)
{
  Q_ASSERT(env);
  SWING_REENTRANT(update, SWING_ENV_ARG)
  if (isQWidget()) {
    ComponentDelegate::update(env);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::update(JNIEnv *env, jint x, jint y, jint width,
                                     jint height)
{
  Q_ASSERT(env);
  SWING_REENTRANT(update, SWING_ENV_ARG, JINT_ARG(x), JINT_ARG(y),
                  JINT_ARG(width), JINT_ARG(height))
  if (isQWidget()) {
    ComponentDelegate::update(env, x, y, width, height);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setUpdatesEnabled(JNIEnv *env, jboolean enabled)
{
  Q_ASSERT(env);
  SWING_REENTRANT(setUpdatesEnabled, SWING_ENV_ARG, JBOOLEAN_ARG(enabled))
  if (isQWidget()) {
    ComponentDelegate::setUpdatesEnabled(env, enabled);
  } else {
    actionData()->updatesEnabled = enabled;
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::polish(JNIEnv *env)
{
  Q_ASSERT(env);
  SWING_REENTRANT(polish, SWING_ENV_ARG)
  if (isQWidget()) {
    ComponentDelegate::polish(env);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setComponentOrientation(JNIEnv *env,
                                                      jint orientation)
{
  Q_ASSERT(env);
  SWING_REENTRANT(setComponentOrientation, SWING_ENV_ARG,
                  JINT_ARG(orientation))
  if (isQWidget()) {
    ComponentDelegate::setComponentOrientation(env, orientation);
  } else {
    actionData()->orientation = orientation;
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setToolTipText(JNIEnv *env, jstring text)
{
  Q_ASSERT(env);
  Q_ASSERT(text);
  SWING_REENTRANT(setToolTipText, SWING_ENV_ARG, JSTRING_ARG(text))
  if (isQWidget()) {
    widget()->setToolTip(QStringAdapter(env, text));
  } else {
    QStringAdapter toolTip(env, text);
    actionData()->toolTip = toolTip;
    action()->setToolTip(toolTip);
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setStatusTipText(JNIEnv *env, jstring text)
{
  Q_ASSERT(env);
  Q_ASSERT(text);
  SWING_REENTRANT(setStatusTipText, SWING_ENV_ARG, JSTRING_ARG(text))
  if (isQWidget()) {
    widget()->setStatusTip(QStringAdapter(env, text));
  } else {
    action()->setStatusTip(QStringAdapter(env, text));
  }
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setWhatsThisText(JNIEnv *env, jstring text)
{
  Q_ASSERT(env);
  SWING_REENTRANT(setWhatsThisText, SWING_ENV_ARG, JSTRING_ARG(text))
  if (isQWidget()) {
    widget()->setWhatsThis(QStringAdapter(env, text));
  } else {
    action()->setWhatsThis(QStringAdapter(env, text));
  }
}

// ActionComponentDelegate :: Protected Member Functions. ----------------------

/*!
 * \brief Deletes QWidget.
 *
 * \return widget the widget to be deleted
 */
void ActionComponentDelegate::deleteQWidget(QWidget *widget)
{
  if (widget) {
    setQObject(0);
    delete widget;
  }
}

/*!
 * \brief Deletes QAction.
 *
 * \return action the action to be deleted
 */
void ActionComponentDelegate::deleteQAction(QAction *action)
{
  setQObject(0);
  ActionComponentData *data = getActionData(m_action);
  if (data) {
    delete data;
  }
  delete action;
}

/*!
 * \brief Switches to QWidget.
 */
void ActionComponentDelegate::switchToQWidget()
{
  if (!m_widget) {
    if (m_action) {
      m_widget = convertToQWidget(m_action);
      deleteQAction(m_action);
      m_action = 0;
    } else {
      m_widget = createQWidget();
    }
    setQObject(m_widget);
  }
}

/*!
 * \brief Switches to QAction.
 */
void ActionComponentDelegate::switchToQAction()
{
  if (!m_action) {
    if (m_widget) {
      m_action = convertToQAction(m_widget);
      deleteQWidget(m_widget);
      m_widget = 0;
    } else {
      m_action = createQAction();
    }
    setQObject(m_action);
  }
}

/*!
 * \brief Converts QAction into QWidget.
 *
 * \return QAction converted into QWidget
 */
QWidget *ActionComponentDelegate::convertToQWidget(QAction *action)
{
  QWidget *widget = createQWidget();
  m_makeVisible = action->isVisible();
  widget->setEnabled(action->isEnabled());
  if (!action->font().isCopyOf(QApplication::font())) {
    widget->setFont(action->font());
  }
  if (!action->statusTip().isEmpty()) {
    widget->setStatusTip(action->statusTip());
  }
  if (!action->whatsThis().isEmpty()) {
    widget->setWhatsThis(action->whatsThis());
  }
  if (eventEnabled(SWING_EVENT(MOUSE_MOTION_EVENT_MASK))) {
    widget->setMouseTracking(true);
  }
  ActionComponentData *data = getActionData(action);
  if (data) {
    if (data->focusable) {
      widget->setFocusPolicy(Qt::StrongFocus);
    } else {
      widget->setFocusPolicy(Qt::NoFocus);
    }
    if (data->bounds.isValid()) {
      widget->setGeometry(data->bounds);
    }
    if (data->maximumSize.isValid()) {
      widget->setMaximumSize(data->maximumSize);
    }
    if (data->minimumSize.isValid()) {
      widget->setMinimumSize(data->minimumSize);
    }
    if (data->fixedSize.isValid()) {
      widget->setFixedSize(data->fixedSize);
    }
    if (data->background.isValid()) {
      QPalette palette(widget->palette());
      palette.setColor(QPalette::Window, data->background);
      widget->setPalette(palette);
    }
    if (data->foreground.isValid()) {
      QPalette palette(widget->palette());
      palette.setColor(QPalette::WindowText, data->foreground);
      widget->setPalette(palette);
    }
    if (!data->toolTip.isEmpty()) {
      widget->setToolTip(data->toolTip);
    }
    if (!data->styleSheet.isEmpty()) {
      widget->setStyleSheet(data->styleSheet);
      QList<QByteArray> dpn = action->dynamicPropertyNames();
      for (int i = 0; i < dpn.size(); i++) {
        if (action->property(dpn.at(i)) == STYLE_SHEET_PROPERTY_VALUE) {
          widget->setProperty(dpn.at(i), STYLE_SHEET_PROPERTY_VALUE);
        }
      }
    }
    widget->setCursor(data->cursor);
    widget->setAutoFillBackground(data->opaque);
    switch(data->orientation) {
      case 1:
        widget->unsetLayoutDirection();
        break;
      case 4:
        widget->setLayoutDirection(Qt::LeftToRight);
        break;
      default:
        widget->setLayoutDirection(Qt::RightToLeft);
        break;
    }
    widget->setUpdatesEnabled(data->updatesEnabled);
  }
  return widget;
}

/*!
 * \brief Converts QWidget into QAction.
 *
 * \return QWidget converted into QAction
 */
QAction *ActionComponentDelegate::convertToQAction(QWidget *widget)
{
  QAction *action = createQAction();
  m_makeVisible = widget->isVisible();
  action->setEnabled(widget->isEnabled());
  action->setFont(widget->font());
  if (!widget->statusTip().isEmpty()) {
    action->setStatusTip(widget->statusTip());
  }
  if (!widget->whatsThis().isEmpty()) {
    action->setWhatsThis(widget->whatsThis());
  }
  action->setVisible(widget->isVisible());
  ActionComponentData *data = createActionData();
  data->focusable = (widget->focusPolicy() != Qt::NoFocus);
  data->opaque = widget->autoFillBackground();
  data->updatesEnabled = widget->updatesEnabled();
  data->bounds = widget->frameGeometry();
  data->maximumSize = widget->maximumSize();
  data->minimumSize = widget->minimumSize();
  data->background = widget->palette().color(QPalette::Window);
  data->foreground = widget->palette().color(QPalette::WindowText);
  if (!widget->toolTip().isEmpty()) {
    action->setToolTip(widget->toolTip());
    data->toolTip = widget->toolTip();
  }
  data->styleSheet = widget->styleSheet();
  data->cursor = widget->cursor();
  if (!data->styleSheet.isEmpty()) {
    QList<QByteArray> dpn = widget->dynamicPropertyNames();
    for (int i = 0; i < dpn.size(); i++) {
      if (widget->property(dpn.at(i)) == STYLE_SHEET_PROPERTY_VALUE) {
        action->setProperty(dpn.at(i), STYLE_SHEET_PROPERTY_VALUE);
      }
    }
  }
  return action;
}

/*!
 * \brief Creates new action data.
 *
 * \return action data
 */
ActionComponentData *ActionComponentDelegate::createActionData()
{
  return new ActionComponentData();
}

/*!
 * \brief Returns \c true if the delegate adaptee is QWidget.
 *
 * \return \c true if the delegate adaptee is QWidget
 */
bool ActionComponentDelegate::isQWidget()
{
  if (m_widget) {
    return true;
  }
  if (m_action) {
    return false;
  }
  m_action = createQAction();
  return false;
}

/*!
 * \brief Returns action data.
 *
 * \return action data
 */
ActionComponentData *ActionComponentDelegate::actionData()
{
  ActionComponentData *data = getActionData(m_action);
  if (!data) {
    data = createActionData();
    m_action->setData(qVariantFromValue(static_cast<void *>(data)));
  }
  return data;
}

/*!
 * \brief Returns action data of the specified action.
 *
 * \param action the action
 *
 * \return action data
 */
ActionComponentData *ActionComponentDelegate::getActionData(QAction *action)
{
  return static_cast<ActionComponentData *>(
                                        qVariantValue<void *>(action->data()));
}

// ActionComponentDelegate :: Overridden Function Members. ---------------------

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::setParentDelegate(ContainerDelegate *parentDelegate)
{
  ComponentDelegate::setParentDelegate(parentDelegate);
  if (m_makeVisible) {
    if (isQWidget()) {
      widget()->setVisible(true);
    } else {
      action()->setVisible(true);
    }
  }
}

/*!
 * \inheritDoc
 */
QWidget *ActionComponentDelegate::qWidget()
{
  switchToQWidget();
  return m_widget;
}

/*!
 * \inheritDoc
 */
QAction *ActionComponentDelegate::qAction()
{
  switchToQAction();
  return m_action;
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::objectDestroyed(QObject *qObj) {
  m_widget = 0;
  ComponentDelegate::objectDestroyed(qObj);
}

/*!
 * \inheritDoc
 */
void ActionComponentDelegate::filterEnabledEvents()
{
  if (isQWidget()) {
    ComponentDelegate::filterEnabledEvents();
  }
}
